#ifndef F103RC_WS2812B_WS2812_HPP
#define F103RC_WS2812B_WS2812_HPP

#include <stm32f1xx_hal.h>
#include <stdint.h>
#include <color_utils.hpp>

#define WS_NLEDS (5 * 30)

struct ws_byte {
    uint8_t data[3];

    ws_byte operator=(uint8_t i) {
        /*
         * convert 1 byte to SPI tx data.
         * 1 = 3b110 0=3b100, only the 2nd bit is different.
         * We make data = 24b100100...100100, and set the 2nd bit accordingly.
         */
        data[0] = 0x92; // 8b10010010
        data[1] = 0x49; // 8b01001001
        data[2] = 0x24; // 8b00100100
        if (i & 0x80)
            data[0] |= (1 << 6);
        if (i & 0x40)
            data[0] |= (1 << 3);
        if (i & 0x20)
            data[0] |= 1;
        if (i & 0x10)
            data[1] |= (1 << 5);
        if (i & 0x8)
            data[1] |= (1 << 2);
        if (i & 0x4)
            data[2] |= (1 << 7);
        if (i & 0x2)
            data[2] |= (1 << 4);
        if (i & 0x1)
            data[2] |= (1 << 1);
        return *this;
    }
};

struct ws_tx_entry {
    ws_byte data[3];
    uint8_t dummy = 0;

    ws_tx_entry &operator=(const CRGB &e) {
        data[0] = e.g;
        data[1] = e.r;
        data[2] = e.b;
        return *this;
    }
};

class WS2812 final {
private:
    CRGB color_buffer[WS_NLEDS];
    ws_tx_entry tx_buffer[WS_NLEDS];
    SPI_HandleTypeDef *spi;
    volatile bool render_complete;
public:

    WS2812(SPI_HandleTypeDef *_s) : spi(_s) {}

    void clear() {
        for (auto &c:color_buffer) {
            c.r = 0;
            c.g = 0;
            c.b = 0;
        }
    }

    void fade(float rate) {
        for (auto &c:color_buffer) {
            c *= rate;
        }
    }

    void render();

    void drawPixel(float pos, float size, CRGB color);

    void renderComplete() { render_complete = true; }

    void init();
};

#endif //F103RC_WS2812B_WS2812_HPP
